
#include "Direct3D.h"
#include "D3DUtility.h"

Direct3D Direct3D::m_siInstance;

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Constructor
//
Direct3D::Direct3D()
{
	m_lpDirect3DDev	=	NULL;
	m_lpDirect3DObj	=	NULL;
	m_lpSprite		=	NULL;
	m_lpLine		=	NULL;
	memset(&m_dppPresentParams, 0, sizeof(D3DPRESENT_PARAMETERS));
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Destructor
//
Direct3D::~Direct3D()
{
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		GetInstance
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			A pointer to the instance of the class.
//
//	Purpose:		Returns a pointer to the single instance.
//
Direct3D* Direct3D::GetInstance()
{
	return &m_siInstance;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Accessors
//
//	Last Modified:	03/05/2011
//
LPDIRECT3D9						Direct3D::GetD3DObject()	{ return m_lpDirect3DObj;		}
LPDIRECT3DDEVICE9				Direct3D::GetD3DDevice()	{ return m_lpDirect3DDev;		}
LPD3DXSPRITE					Direct3D::GetSprite()		{ return m_lpSprite;			}
LPD3DXLINE						Direct3D::GetLine()			{ return m_lpLine;				}
const D3DPRESENT_PARAMETERS*	Direct3D::GetParams()		{ return &m_dppPresentParams;	}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Initalize
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	hWnd			A handle to the window to initialize
//									Direct3D in.
//					screenWidth		The width to initalize the window to.
//					screenHeight	The height to initalize the window to.
//					windowMode		The screen mode to initialize the window to.
//					vsyncOn			Determines if the window has vsynced display mode
//									on or off.
//
//	Return:			True if it was successful.
//
//	Purpose:		Initalizes the D3DObject.
//
bool Direct3D::Initalize(HWND hWnd, int screenWidth, int screenHeight, bool windowMode, bool VsyncOn)
{
	// Make sure the hWnd is valid.
	if( !hWnd )
		return false;

	// Set the window handle to the one passed in.
	m_hWnd = hWnd;

	// Create the Direct3D9 Object to the version supported by the SDK Version.
	m_lpDirect3DObj = Direct3DCreate9(D3D_SDK_VERSION);

	// Make sure the object is valid.
	if( !m_lpDirect3DObj )
		DXERROR("Failed to create a Direct3D Object.");

	// Setup the parameters for Direct3D
	m_dppPresentParams.BackBufferWidth = screenWidth;
	m_dppPresentParams.BackBufferHeight = screenHeight;
	m_dppPresentParams.BackBufferFormat = (windowMode) ? D3DFMT_UNKNOWN : D3DFMT_R8G8B8;
	m_dppPresentParams.BackBufferCount = 1;
	m_dppPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
	m_dppPresentParams.MultiSampleQuality = 0;
	m_dppPresentParams.SwapEffect = D3DSWAPEFFECT_COPY;
	m_dppPresentParams.hDeviceWindow = hWnd;
	m_dppPresentParams.Windowed = windowMode;
	m_dppPresentParams.EnableAutoDepthStencil = false;
	m_dppPresentParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	m_dppPresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	m_dppPresentParams.PresentationInterval = (VsyncOn) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;

	// Create the Direct3D Device.
	if( FAILED( m_lpDirect3DObj->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
			D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_dppPresentParams, &m_lpDirect3DDev) ) )
		DXERROR("Failed to create a Direct3D device.");

	// Create Sprite Object.
	if( FAILED( D3DXCreateSprite(m_lpDirect3DDev, &m_lpSprite)))
		DXERROR("Failed to create a sprite object.");

	// Create the Line Object.
	if( FAILED( D3DXCreateLine(m_lpDirect3DDev, &m_lpLine)))
		DXERROR("Failed to create a line object.");

	m_lpLine->SetAntialias(TRUE);
	m_lpLine->SetWidth(3.0f);

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Shutdown
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			void
//
//	Purpose:		Shuts down the D3DObject.
//
void Direct3D::Shutdown()
{
	SRELEASE(m_lpLine);
	SRELEASE(m_lpSprite);
	SRELEASE(m_lpDirect3DDev);
	SRELEASE(m_lpDirect3DObj);
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		ScreenClear
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	red		The amount of red to clear the screen to.
//					blue	The amount of blue to clear the screen to.
//					green		The amount of green to clear the screen to.
//
//	Return:			void
//
//	Purpose:		Clears the screen to the given color.
//
void Direct3D::ScreenClear(uChar red, uChar blue, uChar green)
{
	m_lpDirect3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, red, blue, green), 1.0f, 0);

	if( m_lpDirect3DDev->TestCooperativeLevel() == D3DERR_DEVICENOTRESET )
	{
		m_lpLine->OnLostDevice();
		m_lpSprite->OnLostDevice();
		m_lpDirect3DDev->Reset(&m_dppPresentParams);
		m_lpSprite->OnResetDevice();
		m_lpLine->OnResetDevice();
	}
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		DeviceBegin
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Starts the device for rendering.
//
bool Direct3D::DeviceBegin()
{
	if( FAILED( m_lpDirect3DDev->BeginScene() ) )
		DXERROR("Failed to begin device scene.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		SpriteBegin
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Starts the sprite for rendering.
//
bool Direct3D::SpriteBegin()
{
	if( FAILED( m_lpSprite->Begin(D3DXSPRITE_ALPHABLEND) ) )
		DXERROR("Failed to begin sprite object.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		LineBegin
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Starts the sprite for rendering.
//
bool Direct3D::LineBegin()
{
	if( FAILED( m_lpLine->Begin() ) )
		DXERROR("Failed to begin line object.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		DeviceEnd
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Ends the device for rendering.
//
bool Direct3D::DeviceEnd()
{
	if( FAILED( m_lpDirect3DDev->EndScene() ) )
		DXERROR("Failed to end device scene.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		SpriteEnd
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Ends the sprite for rendering.
//
bool Direct3D::SpriteEnd()
{
	if( FAILED( m_lpSprite->End() ) )
		DXERROR("Failed to end sprite object.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		LineEnd
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			Returns true if it's successful.
//
//	Purpose:		Ends the line for rendering.
//
bool Direct3D::LineEnd()
{
	if( FAILED( m_lpLine->End() ) )
		DXERROR("Failed to end line object.")

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		Present
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	void
//
//	Return:			void
//
//	Purpose:		Renders the back buffer to the screen.
//
void Direct3D::Present()
{
	m_lpDirect3DDev->Present(NULL,NULL,NULL,NULL);
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		ChangeParameters
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	width		The width to change the screen to.
//					height		The height to change the screen to.
//					windowMode	The mode to change the window to.
//
//	Return:			void
//
//	Purpose:		Changes the display parameters of the screen.
//
void Direct3D::ChangeParameters(int width, int height, bool windowMode)
{
	m_dppPresentParams.BackBufferFormat = (windowMode) ? D3DFMT_UNKNOWN : D3DFMT_R8G8B8;
	m_dppPresentParams.BackBufferWidth = width;
	m_dppPresentParams.BackBufferHeight = height;
	m_dppPresentParams.Windowed = windowMode;

	m_lpLine->OnLostDevice();
	m_lpSprite->OnLostDevice();
	m_lpDirect3DDev->Reset(&m_dppPresentParams);
	m_lpSprite->OnResetDevice();
	m_lpLine->OnResetDevice();

	DWORD windowStyleFlags = WS_VISIBLE;

	if( windowMode )
	{
		windowStyleFlags |= WS_OVERLAPPEDWINDOW;
		ShowCursor(TRUE);
	}
	else
	{
		windowStyleFlags |= WS_POPUP;
		ShowCursor(FALSE);
	}

	SetWindowLong(m_hWnd, GWL_STYLE, windowStyleFlags);

	if( windowMode )
	{
		RECT window;
		window.left = 0;
		window.top = 0;
		window.right = width;
		window.bottom = height;

		AdjustWindowRectEx(&window, windowStyleFlags, FALSE, WS_EX_APPWINDOW);

		int windowWidth = window.right - window.left, 
			windowHeight = window.bottom - window.top;

		SetWindowPos(m_hWnd, HWND_TOP, (GetSystemMetrics(SM_CXSCREEN)>>1) - (windowWidth>>1),
			(GetSystemMetrics(SM_CYSCREEN)>>1) - (windowHeight>>1), windowWidth, windowHeight, SWP_SHOWWINDOW);
	}
	else
	{
		SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
	}
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		DrawRect
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	rect		The rectangle used to draw.
//					red			The amount of red to draw the rectangle.
//					blue		The amount of blue to draw the rectangle.
//					green		The amount of green to draw the rectangle.
//
//	Return:			void
//
//	Purpose:		Draws a rectangle of the specified color.
//
void Direct3D::DrawRect(RECT rect, uChar red, uChar blue, uChar green)
{
	D3DRECT d3dRect;
	d3dRect.x1 = rect.left;
	d3dRect.y1 = rect.top;
	d3dRect.x2 = rect.right;
	d3dRect.y2 = rect.bottom;

	m_lpDirect3DDev->Clear(1, &d3dRect, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, red, blue, green), 1.0f, 0);
}

///////////////////////////////////////////////////////////////////////////////////////
//	Function:		DrawLine
//
//	Last Modified:	03/05/2011
//
//	Parameter(s):	x1			The x of the starting point.
//					y1			The y of the starting point.
//					x2			The x of the ending point.
//					y2			The y of the ending point.
//					red			The amount of red to draw the rectangle.
//					blue		The amount of blue to draw the rectangle.
//					green		The amount of green to draw the rectangle.
//
//	Return:			void
//
//	Purpose:		Draws a line of the specified color.
//
void Direct3D::DrawLine(int x1, int y1, int x2, int y2, uChar red, uChar blue, uChar green)
{
	D3DXVECTOR2 line[2];

	line[0].x = (float)x1;
	line[0].y = (float)y1;
	line[1].x = (float)x2;
	line[1].y = (float)y2;

	m_lpLine->Draw(line, 2, D3DCOLOR_ARGB(255, red, blue, green));
}